package net.homeip.jcperera.nu.core.boot;

import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.Hashtable;

import javax.ejb.EJB;
import javax.ejb.LocalBean;
import javax.ejb.Stateless;
import javax.ejb.TransactionManagement;
import javax.ejb.TransactionManagementType;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Named;

import net.homeip.jcperera.nu.core.CoreBase;
import net.homeip.jcperera.nu.core.config.Configuration;
import net.homeip.jcperera.nu.core.msg.BaseMessage;
import net.homeip.jcperera.nu.core.msg.ErrorMessage;
import net.homeip.jcperera.nu.core.msg.Logger;

/**
 * Session Bean implementation class Startup<br/>
 * Initializes and Loads System Wide Entities into System
 * 
 * @name BootBean class
 * @author julio.c.perera@gmail.com
 * @application nu
 * @since 0.1.0.0
 */

@Stateless
@TransactionManagement(TransactionManagementType.BEAN)
@ApplicationScoped
@LocalBean
@Named(Boot.componentName)
public class Boot extends CoreBase implements Serializable
{
    // Required to implement Serializable interface
    private static final long   serialVersionUID  = -6772524705983605617L;

    // CoreBase properties, for easy use from other components, as well as logging
    public static final int     runtimeLevel      = 0;
    public static final String  moduleName        = "core";
    public static final String  componentName     = "nu_core_boot_bean";

    // Get the log
    private final static Logger log               = Logger.getLogger();

    public static final String  BASE_CONF_CONTEXT = "nu.boot.sequence.include";

    // Status of Configuration
    public enum Status {
        NEW, INITIALIZING, READY, SHUTTINGDOWN
    };

    ;
    
    private Status        status = Status.NEW;
    private Configuration configuration;

    @EJB
    private System        system;
    private static System systemRef;

    public Boot()
    {
    }

    private void setStatus(Status newStatus)
    {
        this.status = newStatus;
        log.info("Status is now " + this.status);
    }

    // Getter for status
    public Status getStatus()
    {
        return (this.status);
    }

    public void start(String nudbclass, String nudburl, String nudbuser, String nudbpass) throws Exception
    {
        systemRef = system;
        log.info("-- NU: Starting NU framework... ---");
        setStatus(Status.INITIALIZING);
        system.start(nudbclass, nudburl, nudbuser, nudbpass);
        configuration = system.getConfiguration();
        bootApplicationSpecificClasses();
        setStatus(Status.READY);
        log.info("-- NU: Finished Starting, ready ---");
    }

    public void stop()
    {
        log.info("-- NU: Stopping NU framework... ---");
        setStatus(Status.SHUTTINGDOWN);
        system.stop();
        log.info("-- NU: Finished Stopping ---");
    }

    /**
     * <p>
     * Boots application specific classes as stated in the Configuration, the configuration is assumed to be in the
     * following format, note that <code>#number#</code> starts from one:
     * </p>
     * <ul>
     * <li><code>application.boot.sequence.include.total</code> = Total of components to launch at startup (boot), it
     * could be zero (0)</li>
     * <li><code>application.boot.sequence.include.#number#.name</code> = Name (to show on log) of the component to
     * launch</li>
     * <li><code>application.boot.sequence.include.#number#.class.name</code> = ClassName (to call
     * <code>getInstance()</code>) of the component to launch</li>
     * </ul>
     * <p>
     * <b>Note:</b> The component to boot MUST specify an <code><i>static</i> <b>getInstance()</b></code> method. You
     * should specify correctly these data or the booting process will fail, in case you not specify anything,
     * <code>total</code> assumes zero and no custom beans are launched at boot.
     * </p>
     */
    private void bootApplicationSpecificClasses() throws Exception
    {
        int qtyDataItems = Integer.parseInt(configuration.get(BASE_CONF_CONTEXT + ".total", "0"));
        for (int index = 1; index <= qtyDataItems; index++)
        {
            Hashtable<String, String> extensions = new Hashtable<String, String>();
            extensions.put("%index%", Integer.toString(index));
            String thisBootItemPath = BASE_CONF_CONTEXT + "." + String.valueOf(index);

            // .class.name
            String className = this.configuration.get(thisBootItemPath + ".class.name", "").trim();
            if (className.length() == 0)
                throw new ErrorMessage("nu.core.boot.custom.sequence.class.name.empty",
                        BaseMessage.FRIENDLINESS_TECHNICAL, extensions);

            // .name
            String name = this.configuration.get(thisBootItemPath + ".name", "").trim();
            if (name.length() == 0)
            {
                extensions.put("%className%", className);
                throw new ErrorMessage("nu.core.boot.custom.sequence.name.empty", BaseMessage.FRIENDLINESS_TECHNICAL,
                        extensions);
            }

            // Now, proceed to instantiate the component
            log.info("Getting " + name + "...");
            Method method = Class.forName(className).getMethod("getInstance");
            method.invoke(null, (Object[]) null);
        }
    }

    public static System getSystem()
    {
        return systemRef;
    }
}
